#include "dirs.hpp"
#include "lines.hpp"
#include <cmath>
#include <cstdint>
#include <ctime>
#include <stdio.h>
#include <time.h>

extern price_t g_hop;

namespace Trade {
void
SpotDirAndTime::print(const char* title, const time_t t_now) const noexcept
{
  static const char* type_str[] = {
    "unkown",
    "no_change",
    "possible",
    "change_dir",
  };
  printf("Possible %s ==> %s, cur-dir %d CTD %d, %d, %d ==> possible %d\n",
         title,
         type_str[uint8_t(ctype)],
         cur_dir,
         change_to_dir,
         (after_timer ? int(after_timer - t_now) / 60 : -1234),
         durantion_timer ? int(durantion_timer - t_now) / 60 : -1234,
         possible_dir);
}

void
StageDirs::check_zero_dir() noexcept
{
  gb_no_mid = gb_no_ein = false;
  if (!mid_dir && ein_dir) {
    mid_dir = (ts_mid_l.d) ? ts_mid_l.d : ein_dir;
    gb_no_mid = true;
  }
  if (mid_dir && !ein_dir) {
    ein_dir = (ts_ein_l.d) ? ts_ein_l.d : mid_dir;
    gb_no_ein = true;
  }
  if (!ts_mid_l.d)
    ts_mid_l.d = mid_dir;
  if (!ts_ein_l.d)
    ts_ein_l.d = ein_dir;
}

void
StageDirs::print(const time_t t,
                 const dir_t w,
                 const int64_t idx) const noexcept
{
  auto tt = [&t](const time_t os) {
    return (os) ? int64_t(os - t) / 60 : -1234;
  };

  printf(
    "\033[38;5;12m-- D --\033[0m MID %d Ein%d, B %d, W %d | %d %d | g%ld\n",
    mid_dir,
    ein_dir,
    big_dir,
    w,
    gb_no_mid,
    gb_no_ein,
    idx);
  if (n_t1 > 0)
    printf("  %d T11(%ld, %ld, %ld)",
           ts_mid_l.d,
           tt(ts_mid_l.t[0]),
           tt(ts_mid_l.t[1]),
           tt(ts_mid_l.t[2]));
  if (n_t1 > 1)
    printf(", %d T12(%ld, %ld, %ld)",
           ts_mid_r.d,
           tt(ts_mid_r.t[0]),
           tt(ts_mid_r.t[1]),
           tt(ts_mid_r.t[2]));
  printf(" | ");
  if (n_t2 > 0)
    printf("%d T21(%ld, %ld, %ld)",
           ts_ein_l.d,
           tt(ts_ein_l.t[0]),
           tt(ts_ein_l.t[1]),
           tt(ts_ein_l.t[2]));
  if (n_t2 > 1)
    printf(", %d T22(%ld, %ld, %ld)",
           ts_ein_r.d,
           tt(ts_ein_r.t[0]),
           tt(ts_ein_r.t[1]),
           tt(ts_ein_r.t[2]));
  printf("\n");
}

bool
StageDirs::is_3same_dir_(const dir_t d) const noexcept
{
  return (d == mid_dir && d == ein_dir);
}

bool
StageDirs::is_2same_dir_(const dir_t d) const noexcept
{
  return (!(d == mid_dir && d == ein_dir) && (d == mid_dir || d == ein_dir));
}

bool
StageDirs::is_opposite_dir_(const dir_t d) const noexcept
{
  return (d && d != mid_dir && d != ein_dir);
}

bool
StageDirs::have_zero_dir_() const noexcept
{
  return (!mid_dir || !ein_dir);
}

bool
StageDirs::both_zero_dir_() const noexcept
{
  return (!mid_dir && !ein_dir);
}

time_t
StageDirs::timer_to_mid_dir(const dir_t d) const noexcept
{
  time_t tm = 0;
  return tm;
}

time_t
StageDirs::timer_to_ein_dir(const dir_t d) const noexcept
{
  time_t tm = 0;
  return tm;
}

dir_t
StageDirs::dir_to_mid_next(const dir_t d) const noexcept
{
  dir_t dir = 0;
  return dir;
}

dir_t
StageDirs::dir_to_ein_next(const dir_t d) const noexcept
{
  dir_t dir = 0;
  return dir;
}

dir_t
StageDirs::dirs_mid(time_t& tm, const dir_t d /* = 0 */) const noexcept
{
  dir_t dir = 0;
  tm = 0;
  return dir;
}

dir_t
StageDirs::dirs_next_mid(time_t& tm, const dir_t d /* = 0 */) const noexcept
{
  dir_t dir = 0;
  tm = 0;
  return dir;
}

dir_t
StageDirs::dirs_ein(time_t& tm, const dir_t d /* = 0 */) const noexcept
{
  dir_t dir = 0;
  tm = 0;
  return dir;
}

dir_t
StageDirs::dirs_next_ein(time_t& tm, const dir_t d /* = 0 */) const noexcept
{
  dir_t dir = 0;
  tm = 0;
  return dir;
}

bool
StageDirs::is_possible_to_change(SpotDirAndTime& pdc_mid,
                                 SpotDirAndTime& pdc_ein,
                                 const time_t t_now) noexcept
{
  auto mid_change = is_possible_to_change__(pdc_mid, t_now, true);
  auto ein_change = is_possible_to_change__(pdc_ein, t_now, false);

  if ((pdc_mid.change_to_dir != pdc_ein.change_to_dir) ||
      std::abs(pdc_mid.after_timer - pdc_ein.after_timer) > 1200) {
  }
  return (mid_change == DIR_CHANGED_TYPE::CHANGE_DIR ||
          ein_change == DIR_CHANGED_TYPE::CHANGE_DIR);
}

DIR_CHANGED_TYPE
StageDirs::is_possible_to_change__(SpotDirAndTime& pdc,
                                   const time_t t_now,
                                   const bool is_mid) noexcept
{
  pdc.reset();
  auto n = (is_mid) ? n_t1 : n_t2;

  if (n > 1) {
    TimerSpot& ts_l = (is_mid) ? ts_mid_l : ts_ein_l;
    TimerSpot& ts_r = (is_mid) ? ts_mid_r : ts_ein_r;
    auto idx_l = ts_l.avaliable_index(t_now);
    auto idx_r = ts_r.avaliable_index(t_now);

    if (TimerSpot::is_avaliable(idx_l)) {
      pdc.cur_dir = ts_l.d;
      pdc.cur_idx = idx_l;
      if (idx_l > 0 || ts_l.t[0] - t_now <= MINUTES_10)
        pdc.possible_dir = ts_r.d;
    } else if (TimerSpot::is_avaliable(idx_r)) {
      pdc.cur_dir = ts_r.d;
      pdc.cur_idx = idx_r;
    }

    if (TimerSpot::is_avaliable(idx_l) && TimerSpot::is_avaliable(idx_r)) {
      if (ts_r.d != ts_l.d) {
        pdc.ctype = DIR_CHANGED_TYPE::CHANGE_DIR;
        pdc.change_to_dir = ts_r.d;
        pdc.durantion_timer = ts_r.t[idx_r];
      } else {
        pdc.ctype = DIR_CHANGED_TYPE::NO_CHANGE;
      }
      pdc.after_timer = ts_l.t[idx_l];
    } else if (!TimerSpot::is_avaliable(idx_l) &&
               TimerSpot::is_avaliable(idx_r)) {
      pdc.ctype =
        (idx_r == 0) ? DIR_CHANGED_TYPE::NO_CHANGE : DIR_CHANGED_TYPE::POSSIBLE;
      pdc.change_to_dir = 0;
      pdc.after_timer = ts_r.t[idx_r];
      pdc.durantion_timer = 0;
    }
  } else if (n > 0) {
    auto idx_l = ts_mid_l.avaliable_index(t_now);
    if (TimerSpot::is_avaliable(idx_l)) {
      pdc.ctype =
        (idx_l == 0) ? DIR_CHANGED_TYPE::NO_CHANGE : DIR_CHANGED_TYPE::POSSIBLE;
      pdc.change_to_dir = 0;
      pdc.after_timer = ts_mid_l.t[idx_l];
      pdc.durantion_timer = 0;
      pdc.cur_dir = ts_mid_l.d;
      pdc.cur_idx = idx_l;
    }
  }

  return pdc.ctype;
}

// int8_t
// StageDirs::retrive_stage_dir_timer(const dir_t d,
//                                    const time_t t_now,
//                                    TimeAfterTime& tat) noexcept
// {
//   int8_t ret = 0;
//   tat.reset();

//   auto get_time = [&](const TimerSpot& ts) {
//     time_t tm = 0;
//     if (longer_than(ts.t[0], t_now, MINUTES_10)) {
//       tm = ts.t[0];
//     } else if (longer_than(ts.t[1], t_now, MINUTES_10)) {
//       tm = ts.t[1];
//     } else if (longer_than(ts.t[2], t_now, MINUTES_10)) {
//       tm = ts.t[2];
//     }
//     return tm;
//   };

//   auto get_ts_timer =
//     [&](DirsTimer& dt, const TimerSpot& ts_l, const TimerSpot& ts_r) {
//       dt.cur_dir = (ts_l.d) ? ts_l.d : d;
//       dt.cur_duration = get_time(ts_l);
//       dt.next_dir = ts_r.d;
//       dt.next_duration = get_time(ts_r);
//     };

//   get_ts_timer(tat.dt_mid, ts_mid_l, ts_mid_r);
//   get_ts_timer(tat.dt_ein, ts_ein_l, ts_ein_r);

//   return ret;
// }

void
PossibleDirChange::print(const time_t t_now) const noexcept
{
  mid.print("Mid", t_now);
  ein.print("Ein", t_now);
}

uint8_t
PossibleDirChange::is_going_to_dir(const dir_t d,
                                   const time_t t_now) const noexcept
{
  uint32_t case_no = 0;
  if (mid.ctype != DIR_CHANGED_TYPE::CHANGE_DIR &&
      ein.ctype != DIR_CHANGED_TYPE::CHANGE_DIR && mid.cur_dir != -d &&
      ein.cur_dir != -d) {
    // boteh do not change dir and current dir go to d (did not goto -d)
    case_no = 1;
  } else if (mid.cur_dir == d && ein.cur_dir == d) {
    auto remained_time = get_minimal_time_to_cur_dir(d, t_now);
    case_no = (longer_than(remained_time, t_now, MINUTES_10)) ? 2 : 3;
  }
  return case_no;
}

uint8_t
PossibleDirChange::possiblely_goto_dir(const dir_t d,
                                       const time_t t_now
                                       [[maybe_unused]]) const noexcept
{
  uint32_t case_no = 0;
  if (mid.change_to_dir == d && ein.change_to_dir == d) {
    if ((mid.possible_dir == d && ein.possible_dir == d)) {
      // PDCs chang to d and possiblely running to d now
      case_no = 1;
    }
  } else if (mid.possible_dir == d || ein.possible_dir == d) {
    case_no = 2;
  }

  return case_no;
}

bool
PossibleDirChange::one_possible_to_dir(const dir_t d) const noexcept
{
  return ((mid.cur_dir == d || ein.cur_dir == d) ||
          (mid.possible_dir == d || ein.possible_dir == d));
}

dir_t
PossibleDirChange::current_dir() const noexcept
{
  if (mid.cur_dir && mid.cur_dir == ein.cur_dir) {
    return mid.cur_dir;
  }

  return (0);
}

dir_t
PossibleDirChange::possible_dir() const noexcept
{
  if (mid.possible_dir && mid.possible_dir == ein.possible_dir &&
      mid.durantion_timer && ein.durantion_timer) {
    return mid.possible_dir;
  } else
    return (0);
}

bool
PossibleDirChange::no_mid_ein() const noexcept
{
  return (ein.cur_dir == 0 && ein.possible_dir == 0 && mid.cur_dir == 0 &&
          mid.possible_dir == 0);
}

uint8_t
PossibleDirChange::confused() const noexcept
{
  uint32_t case_no = 0;
  if (mid.cur_dir == -ein.cur_dir && mid.change_to_dir == -ein.change_to_dir) {
    case_no = 1;
  } else if (mid.cur_dir == -ein.cur_dir && !mid.change_to_dir &&
             !ein.change_to_dir && mid.possible_dir != ein.possible_dir) {
    case_no = 2;
  } else if (mid.cur_dir != ein.cur_dir &&
             mid.change_to_dir != ein.change_to_dir &&
             mid.possible_dir != ein.possible_dir) {
    case_no = 3;
  }
  return case_no;
}

time_t
PossibleDirChange::get_minimal_time_to_cur_dir(
  const dir_t d,
  const time_t t_now) const noexcept
{
  if (mid.cur_dir == ein.cur_dir && mid.cur_dir == d) {
    return std::min(mid.after_timer, ein.after_timer);
  }
  return 0;
}

dir_t
PossibleDirChange::guess_the_dir(const dir_t d,
                                 const time_t t_now) const noexcept
{
  dir_t dir = 0;
  auto a = is_going_to_dir(d, t_now);
  auto b = possiblely_goto_dir(-d, t_now);
  auto c = is_going_to_dir(-d, t_now);
  auto e = possiblely_goto_dir(d, t_now);

  if ((a || e) && !c && !b)
    dir = d;
  else if (!a && !e && (c || b))
    dir = -d;

  printf("ABCD %u %u %u %u ==> %d\n", a, b, c, e, dir);

  return dir;
}

uint8_t
PossibleDirChange::time_is_very_short(const time_t t_now) const noexcept
{
  uint8_t case_no = 0;
  if (mid.cur_dir == ein.cur_dir && mid.change_to_dir == ein.change_to_dir) {
    if (less_than(mid.after_timer, t_now, MINUTES_10) &&
        less_than(ein.after_timer, t_now, MINUTES_10) &&
        less_than(mid.durantion_timer, mid.after_timer, MINUTES_15) &&
        less_than(ein.durantion_timer, ein.after_timer, MINUTES_15)) {
      case_no = 1;
    }
  }
  return case_no;
}

dir_t
DirList::get_first_dir() const noexcept
{
  if (dm_list.empty())
    return (0);
  else
    return dm_list.front()->dir;
}

bool
DirList::new_dir_maker(const dir_t d,
                       const time_t tm,
                       const price_t hpx) noexcept
{
  bool add = false;
  if (dm_list.empty())
    add = true;
  else {
    if (d != dm_list.front()->dir && tm >= dm_list.front()->tm) {
      add = true;
    }
  }

  if (add) {
    if (dm_list.size() >= _deep) {
      dm_list.pop_back();
    }
    auto bd = std::make_shared<DirMarker>(d, tm, hpx);
    dm_list.push_front(bd);

    durantion(d, tm);
  }

  return add;
}

DirMarkerPtr
DirList::get_first() const noexcept
{
  if (dm_list.empty())
    return nullptr;
  else
    return dm_list.front();
}

DirMarkerPtr
DirList::get_first_opposite() const noexcept
{
  if (dm_list.size() >= 2) {
    auto it = dm_list.begin();
    ++it;
    return (*it);
  } else {
    return nullptr;
  }
}

DirMarkerPtr
DirList::get_first_same_side() const noexcept
{
  if (dm_list.size() >= 3) {
    auto it = dm_list.begin();
    ++it;
    ++it;
    return (*it);
  } else {
    return nullptr;
  }
}

DirMarkerPtr
DirList::get_second_opposite() const noexcept
{
  if (dm_list.size() >= 4) {
    auto it = dm_list.begin();
    ++it;
    ++it;
    ++it;
    return (*it);
  } else {
    return nullptr;
  }
}

DirMarkerPtr
DirList::get_second_same_side() const noexcept
{
  if (dm_list.size() >= 5) {
    auto it = dm_list.begin();
    ++it;
    ++it;
    ++it;
    ++it;
    return (*it);
  } else {
    return nullptr;
  }
}

bool
DirList::is_swing_dir(const time_t t_now) const noexcept
{
  return (get_first_durantion(t_now) < 40);
}

bool
DirList::first_time_is_good(const time_t t_now_) const noexcept
{
  if (dm_list.empty())
    return false;

  // return (dm_list.front()->tm < t_now_ &&
  //         (t_now_ - dm_list.front()->tm < 2 * AN_HOUR + 15 * 60));
  return true;
}

bool
DirList::first_pdc_time_is_good(const time_t t_now) const noexcept
{
  if (dm_list.empty())
    return false;
  if (dm_list.size() > 1) {
    return ((t_now - dm_list.front()->tm >= MINUTES_10) ||
            get_first_durantion(t_now) >= MINUTES_30);
  } else {
    return true;
  }
  return true;
}
int
DirList::get_first_durantion(const time_t t_now) const noexcept
{
  if (dm_list.empty())
    return (-1);

  return int(t_now - dm_list.front()->tm) / 60;
}

dir_t
DirList::get_second_dir() const noexcept
{
  if (dm_list.size() >= 2) {
    auto it = dm_list.begin();
    ++it;
    return (*it)->dir;
  }
  return (0);
}

int
DirList::get_second_durantion() const noexcept
{
  int dur = 0;
  if (dm_list.size() >= 2) {
    auto first = dm_list.front();
    auto it = dm_list.begin();
    ++it;
    dur = int(first->tm - (*it)->tm) / 60;
  }
  return dur;
}

int
DirList::durantion(const dir_t d, const int64_t t_now) noexcept
{
  if (dm_list.empty()) {
    return (0);
  } else if (dm_list.size() == 1) {
    auto i1 = dm_list.begin();
    int delta = t_now - (*i1)->tm;
    if (delta >= MINUTES_30 && (*i1)->dir) {
      long_dir = (*i1)->dir;
      long_dur = delta / 60;
      long_hpx = (*i1)->hpx;
    }
    return long_dur;
  } else {
    long_dir = 0;
    long_dur = 0;
    long_tm = 0;
    long_hpx = 0;

    auto i1 = dm_list.begin();
    int delta = t_now - (*i1)->tm;
    int sum = delta;

    auto i2 = i1;
    ++i1;

    while (i1 != dm_list.end()) {
      if (delta >= MINUTES_30 && (*i1)->dir) {
        long_dir = (*i2)->dir;
        long_dur = delta;
        long_tm = (*i2)->tm;
        long_hpx = (*i2)->hpx;
        // printf(
        //    "    |- long -> dir %d durantion %d\n", long_dir, delta / 60);
        break;
      }

      delta = (*i2)->tm - (*i1)->tm;
      sum += delta;
      ++i1;
      ++i2;
    }

    long_dur /= 60;
    if (d == long_dir)
      sum += delta;
    sum = sum / 60 + get_first_durantion(t_now);

    return sum;
  }
}

void
DirList::print_four(const time_t t_now, const price_t hp_now) const noexcept
{
  int i = 0;
  printf("dd turns: ");
  static const char* str_turn_dir = "-UDBO";
  for (auto it = dm_list.begin(); i < 4 && it != dm_list.end(); ++it, ++i) {
    auto it2 = it;
    ++it2;
    price_t delta = 0;
    if (it2 != dm_list.end())
      delta = (*it)->hpx - (*it2)->hpx;
    printf("%c %d %.02lf;%.02lf, ",
           str_turn_dir[(*it)->dir],
           (*it)->durantion(t_now),
           delta,
           hp_now - (*it)->hpx);
  }
  printf("Long %d %d\n", long_dir, long_dur);
}

void
DirList::print(const char* name, const time_t t_now) const noexcept
{
  printf("%s(%d %dm,%d %dm,%d %dm) ",
         name,
         get_first_dir(),
         get_first_durantion(t_now),
         get_second_dir(),
         get_second_durantion(),
         long_dir,
         long_dur);
}

void
DirList::print_with_hp(const char* name,
                       const time_t t_now,
                       const price_t hp) const noexcept
{
  if (dm_list.empty())
    return;

  printf("%s(", name);
  auto i = dm_list.cbegin();
  printf(
    "%d %dm %.02lf; ", (*i)->dir, int(t_now - (*i)->tm) / 60, hp - (*i)->hpx);
  auto prev_tm = (*i)->tm;
  ++i;
  if (i != dm_list.cend()) {
    printf("%d %dm %.02lf; ",
           (*i)->dir,
           int(prev_tm - (*i)->tm) / 60,
           (hp - (*i)->hpx));
  }
  if (long_dir) {
    printf(
      "%d %dm %.02lf; ", long_dir, int(t_now - long_tm) / 60, (hp - long_hpx));

    dir_t d = 0;
    price_t hpx = 0;
    auto t = get_aux_time(t_now, d, hpx);
    printf("| aux %d %dm %.02lf", d, int(t) / 60, (hp - hpx));
  }

  printf(") ");
}

void
DirList::print(const time_t t_now, const int64_t idx) const noexcept
{
  printf("cur %d dur %d, SEC %d %d, long %d, dur %d g%ld\n",
         get_first_dir(),
         get_first_durantion(t_now),
         get_second_dir(),
         get_second_durantion(),
         long_dir,
         long_dur,
         idx);

  printf(" ------ %ld %ld: \n", idx, t_now);
  auto i1 = dm_list.begin();
  auto i2 = i1;
  ++i2;

  for (; i2 != dm_list.end(); ++i1, ++i2) {
    printf("  1: %d %ld 2: %d %ld 3: %d; \n",
           (*i1)->dir,
           (*i1)->tm,
           (*i2)->dir,
           (*i2)->tm,
           int((*i1)->tm - (*i2)->tm) / 60);
  }
  printf("\n");
}

dir_t
DirList::guess_the_big_dir(const time_t t_now,
                           const price_t hp_now,
                           bool& its_nature) noexcept
{
  if (dm_list.empty())
    return (0);

  auto fdir = get_first_dir();

  dir_t d = 0;
  if (fdir) {
    d = fdir;
  } else {
    d = long_dir;
  }

  its_nature = (d == fdir);

  return d;
}

dir_t
DirList::guess_the_ce_dir(bool& its_nature) const noexcept
{
  dir_t d = 0;
  auto fdir = get_first_dir();
  if (fdir) {
    d = fdir;
  } else {
    d = get_second_dir();
  }

  its_nature = (d == fdir);

  return d;
}

price_t
DirList::get_hp_delta() const noexcept
{
  if (dm_list.size() > 1) {
    auto i = dm_list.begin();
    auto j = i;
    ++j;
    return ((*i)->hpx - (*j)->hpx);
  } else {
    return (0);
  }
}

bool
DirList::long_time_is(const dir_t d, const time_t ts) const noexcept
{
  if (dm_list.empty())
    return (0);
  auto i = dm_list.cbegin();
  if ((*i)->dir == d && (ts - (*i)->tm > MINUTES_45))
    return true;

  if (dm_list.size() < 2)
    return (0);

  ++i;
  if ((*i)->dir == d && (ts - (*i)->tm > MINUTES_45))
    return true;
  return false;
}

bool
DirList::long_long_time(const dir_t d,
                        const time_t ts [[maybe_unused]]) const noexcept
{
  if (dm_list.empty())
    return false;
  bool yes = (!is_swing_dir(ts) && get_first_dir() == d && long_dir == d);
  return yes;
}

time_t
DirList::get_first_time() const noexcept
{
  if (dm_list.empty())
    return (0);
  else
    return (dm_list.front()->tm);
}

time_t
DirList::get_second_time() const noexcept
{
  if (dm_list.size() < 2) {
    return (0);
  } else {
    auto i1 = dm_list.begin();
    i1++;
    return ((*i1)->tm);
  }
}

time_t
DirList::get_long_time() const noexcept
{
  return long_tm;
}

time_t
DirList::get_aux_time(const time_t ts, dir_t& d, price_t& hp) const noexcept
{
  time_t t = 0;
  if (long_dir) {
    auto i = dm_list.cbegin();
    time_t t1 = (*i)->tm;
    t += (ts - t1);
    ++i;

    for (; i != dm_list.cend(); ++i) {
      time_t t2 = (*i)->tm;
      t += (t1 - t2);

      if (t1 - t2 > MINUTES_45) {
        d = (*i)->dir;
        hp = (*i)->hpx;
        break;
      }

      t1 = t2;
    }
  }
  return t;
}

void
DirList::conveter_to_3(DirList3& d3) const noexcept
{
  auto i = dm_list.cbegin();
  if (i != dm_list.cend()) {
    d3.set_d1((*i)->dir, (*i)->tm, (*i)->hpx);
    ++i;

    if (i != dm_list.cend()) {
      d3.set_d2((*i)->dir, (*i)->tm, (*i)->hpx);
    }
  }
  d3.set_long(long_dir, long_tm, long_hpx);
}

void
DirList3::print(const char* name,
                const dir_t d,
                const time_t t_now,
                const price_t hp_now) const noexcept
{
  printf("%s: %d(%d %d %.02lf; %d %d %.02lf; %d)", // %d %.02lf) ",
         name,
         d,
         d1,
         int(t_now - t1) / 60,
         hp_now - hp1,
         d2,
         int(t1 - t2) / 60,
         hp_now - hp2,
         long_dir);
  //  int(t_now - long_tm) / 60,
  //  hp_now - long_hpx);
}

void
DirList3::print(const char* name,
                const time_t t_now,
                const price_t hp_now) const noexcept
{
  printf("%s: (%d, %d %.02lf; %d %d %.02lf; %d) ", //, %d %.02lf) ",
         name,
         d1,
         int(t_now - t1) / 60,
         hp_now - hp1,
         d2,
         int(t1 - t2) / 60,
         hp_now - hp2,
         long_dir);
  //  int(t_now - long_tm) / 60,
  //  hp_now - long_hpx);
}

void
DirList3::print_with_hp(const char* name,
                        const time_t t_now,
                        const price_t hp_now) const noexcept
{
  printf("%s(", name);
  printf("%d %dm %.02lf; ", d1, int(t_now - t1) / 60, hp_now - hp1);
  printf("%d %dm %.02lf; ", d2, int(t2 - t1) / 60, hp_now - hp2);
  printf("%d %dm %.02lf; ",
         long_dir,
         int(t_now - long_tm) / 60,
         (hp_now - long_hpx));
  printf(")\n");
}

bool
DirList3::is_swing_dir(const time_t t_now) const noexcept
{
  if (d1 && t1) {
    return (int(t_now - t1) < (45 * 60));
  } else {
    return true;
  }
}

dir_t
DirList3::get_first_dir() const noexcept
{
  return d1;
}
dir_t
DirList3::get_second_dir() const noexcept
{
  return d2;
}
time_t
DirList3::get_first_time() const noexcept
{
  return t1;
}
time_t
DirList3::get_second_time() const noexcept
{
  return t2;
}
price_t
DirList3::get_first_hp() const noexcept
{
  return hp1;
}
price_t
DirList3::get_second_hp() const noexcept
{
  return hp2;
}
int
DirList3::get_first_durantion(const time_t ts) const noexcept
{
  return (int)(ts - t1) / 60;
}
int
DirList3::get_second_durantion() const noexcept
{
  return (int)(t1 - t2) / 60;
}
void
DirList3::set_d1(const dir_t d, const time_t t, const price_t hp) noexcept
{
  d1 = d;
  t1 = t;
  hp1 = hp;
}
void
DirList3::set_d2(const dir_t d, const time_t t, const price_t hp) noexcept
{
  d2 = d;
  t2 = t;
  hp2 = hp;
}
void
DirList3::set_long(const dir_t d, const time_t t, const price_t hp) noexcept
{
  long_dir = d;
  long_tm = t;
  long_hpx = hp;
}

dir_t
DirList3::possible_dir(const time_t t_now) const noexcept
{
  if (t1 && t2 && (t_now - t1 < MINUTES_30 && t1 - t2 < MINUTES_10))
    return d2;
  return (0);
}

DIR_DREAMER_TYPE
DirList3::possible_dir_type(const time_t t_now) const noexcept
{
  DIR_DREAMER_TYPE ddt = DIR_DREAMER_TYPE::DD_UNKNOW;
  if (t1 && t2 && (t_now - t1 < MINUTES_30 && t1 - t2 < MINUTES_10))
    ddt = DIR_DREAMER_TYPE::DD_SWING;
  else
    ddt = dir_to_ddt(d1);
  ddt_to_dir(ddt);
  return ddt;
}

DIR_DREAMER_TYPE
DirList3::retrive_dir_type(const dir_t cur_dir, const time_t ts) const noexcept
{
  DIR_DREAMER_TYPE ddt = DIR_DREAMER_TYPE::DD_UNKNOW;
  if (!d1)
    return ddt;

  bool is_swing = DirList3::is_swing_dir(ts);
  const dir_t fd = d1;
  if (is_swing) {
    ddt =
      (fd > 0) ? DIR_DREAMER_TYPE::DD_UP_SWING : DIR_DREAMER_TYPE::DD_DN_SWING;
  } else {
    if (cur_dir) {
      ddt = (fd > 0) ? DIR_DREAMER_TYPE::DD_UP : DIR_DREAMER_TYPE::DD_DN;
    } else {
      ddt =
        (fd > 0) ? DIR_DREAMER_TYPE::DD_UP_ZERO : DIR_DREAMER_TYPE::DD_DN_ZERO;
    }
  }
  return ddt;
}

DIR_DREAMER_TYPE
DirList::retrive_dir_type(const dir_t cur_dir, const time_t ts) const noexcept
{
  DIR_DREAMER_TYPE ddt = DIR_DREAMER_TYPE::DD_UNKNOW;
  if (dm_list.empty())
    return ddt;

  bool is_swing = is_swing_dir(ts);
  const dir_t fd = get_first_dir();
  if (is_swing) {
    ddt =
      (fd > 0) ? DIR_DREAMER_TYPE::DD_UP_SWING : DIR_DREAMER_TYPE::DD_DN_SWING;
  } else {
    if (cur_dir) {
      ddt = (fd > 0) ? DIR_DREAMER_TYPE::DD_UP : DIR_DREAMER_TYPE::DD_DN;
    } else {
      ddt =
        (fd > 0) ? DIR_DREAMER_TYPE::DD_UP_ZERO : DIR_DREAMER_TYPE::DD_DN_ZERO;
    }
  }
  return ddt;
}

dir_t
ddt_to_dir(const DIR_DREAMER_TYPE ddt) noexcept
{
  dir_t d = 0;
  switch (ddt) {
    case DD_UP:
    case DD_UP_SWING:
    case DD_UP_ZERO:
      d = 1;
      break;
    case DD_DN:
    case DD_DN_SWING:
    case DD_DN_ZERO:
      d = -1;
    default:
      break;
  }
  return d;
}

DIR_DREAMER_TYPE
dir_to_ddt(const dir_t d) noexcept
{
  DIR_DREAMER_TYPE ddt = DIR_DREAMER_TYPE::DD_UNKNOW;
  if (d > 0)
    ddt = DD_UP;
  else if (d < 0)
    ddt = DD_DN;
  else
    ddt = DD_SWING;
  return ddt;
}

// void
// ForceLists::print(const time_t ts, const price_t hpx) const noexcept
// {
//   printf("Near CmP: ");
//   df_near_78_st.print_with_hp("near_78_st", ts, hpx);
//   df_near_rt_st.print_with_hp("near_rt_st", ts, hpx);

//   printf("\nNear ChG: ");
//   df_near_78_cg.print_with_hp("near_78_cg", ts, hpx);
//   df_near_rt_cg.print_with_hp("near_rt_cg", ts, hpx);

//   printf("\nFar CmP: ");
//   df_far_78_st.print_with_hp("far_78_st", ts, hpx);
//   df_far_rt_st.print_with_hp("far_rt_st", ts, hpx);

//   printf("\nFar ChG: ");
//   df_far_78_cg.print_with_hp("far_78_cg", ts, hpx);
//   df_far_rt_cg.print_with_hp("far_rt_cg", ts, hpx);

//   printf("\n");
// }



time_t
NearFarTrend::get_solved_near_time() const noexcept
{
  time_t t = 0;
  if (t1 && t2)
    t = std::min(t1, t2);
  else if (t1)
    t = t1;
  else if (t2)
    t = t2;
  return t;
}

dir_t
NearFarTrend::get_solved_near_dir() const noexcept
{
  return d_near;
}
time_t
NearFarTrend::get_solved_far_time() const noexcept
{
  time_t t = 0;
  if (t1 && t2)
    t = std::max(t1, t2);
  else if (t1)
    t = t1;
  else if (t2)
    t = t2;
  return t;
}
dir_t
NearFarTrend::get_sovled_far_dir() const noexcept
{
  return d_far;
}
void
NearFarTrend::get_solved_dir_time(dir_t& d_near,
                                  dir_t& d_far,
                                  time_t& t1,
                                  time_t& t2) const noexcept
{
  d_near = this->d_near;
  d_far = this->d_far;
  if (this->t1 && this->t2) {
    if (this->t1 < this->t2) {
      t1 = this->t1;
      t2 = this->t2;
    } else {
      t1 = this->t2;
      t2 = this->t1;
    }
  } else {
    t2 = 0;
    if (this->t1)
      t1 = this->t1;
    else if (this->t2)
      t1 = this->t2;
  }
}

static NearFarTrend old_nft;

event_t
near_far_trend_chagned(const NearFarTrend& nft) noexcept
{
  event_t e = 0;
  if (old_nft.d_near != nft.d_near) {
    e |= EVENT_PREDICTED_NEAR_DIR_CHANGED;
  }
  if (old_nft.d_far != nft.d_far) {
    e |= EVENT_PREDICTED_FAR_DIR_CHANGED;
  }

  if (e) {
    old_nft = nft;
  }
  return e;
}



} // namespace Trade